分布式框架Spark把任务划分到各个子节点进行处理,可以有效利用小机器的CPU来处理大规模数据集。但是Spark也存在局限性,在某些问题的处理上会力不从心,例如两个大数据集的匹配。出现这种问题的原因主要是分布式系统的优势在于机器的数量,子节点的CPU和内存通常配置较低。
问题描述:如果有两个大数据集A和B,假设A有几千万条数据,B有几百万条数据,需要把B中的每一条数据和A中的每一条数据进行比较。下面列举几种解决的方法:
(1)直接把A和B join后两两计算,这样会导致很大的shuffle,千万*百万=十万亿的数量级。
(2)A存到HDFS上,B进行切分,比如每100条起个spark job跑一下。这种方法可以解决大多数问题,比如可以起个spark streaming,然后把B中的数据分片丢进去,再和A中的数据join做运算。这种方式会有较多的shuffle,千万*百=十亿的数量级,所以可以先把数据collect,然后再和A数据做比较。
(3)通常driver可用的内存会比较大,可以先用collect函数把B载入内存,然后把B中的数据分片broadcast到工作节点上,例如每个分片B’包含100条数据,在工作节点上,B’是在内存中的,可以直接和A数据做比较,而不需要join。例如代码:
Broadcast<List<String>> broadcast;
JavaRDD<String> totalResult = null;
int count = 0;
List<String> tempArticleList = new ArrayList<String>();
for (String article : BCollect) { //BCollect:B数据集collect结果
tempArticleList.add(article);
count++;
if (tempArticleList.size() >= 100) {
broadcast = sc.broadcast(tempArticleList);
logger.info("size of broadcast:" + tempArticleList.size());
// baseRddx--A数据集
JavaRDD<String> resultRDDx = baseRddx.flatMap(new CompareArticle());
if (totalResult == null) {
totalResult = resultRDDx;
} else {
totalResult = totalResult.union(resultRDDx);
}
count = 0;
tempArticleList = new ArrayList<String>();
}
}
//把剩下的数据处理掉
if (tempArticleList.size() > 0) {
broadcast = sc.broadcast(tempArticleList);
logger.info("size of broadcast:" + tempArticleList.size());
JavaRDD<String> resultRDDx = baseRddx.flatMap(new CompareArticle());
if (totalResult == null) {
totalResult = resultRDDx;
} else {
totalResult = totalResult.union(resultRDDx);
}
}
class CompareArticle implements FlatMapFunction<String, String> {
@Override
public Iterable<String> call(String line) throws Exception {
List<String> resultList = new ArrayList<String>();
//B数据集分块
List<String> articleList = broadcast.getValue();
...
}
}